// -*- c-basic-offset: 2 default-tab-width: 2 indent-tabs-mode: t -*-
// vim: autoindent tabstop=2 noexpandtab shiftwidth=2 softtabstop=2
//===------------ Array.cc
//
//             This file is part of the reactor project
//                Author: R-Core Team
//
//===---------------------------------------------------------------------===//

#include "Array.h"
#include "Convert.h"
#include "util.h"
#include "RAttributes.h"

namespace R {
	// Duplicate definitions
	//
	template <class T> inline
	Array* duplicateImpl(T* self, bool with_attributes) {
		R_PARAM(self);
		R_VAR(T*, res);
		res = T::doNew(self->length);
		memcpy(res->content, self->content, self->length*sizeof(T::dataType));

		if(with_attributes && (self->attributes != ConstPool::Null)) {
			res->attributes = self->attributes->duplicate(true);
		}
		return res;
	}

	template <> inline
	Array* duplicateImpl(Vector* self, bool with_attributes) {
		R_PARAM(self);
		R_VAR(Vector*, res);

		res = Vector::doNew(self->length);
		for(int i = 0; i < self->length; i++) {
			res->content[i] = (self->content[i] == ConstPool::Null) ?
					ConstPool::Null :
					self->content[i]->duplicate(with_attributes);
		}
		if(with_attributes && (self->attributes != ConstPool::Null)) {
			res->attributes = self->attributes->duplicate(true);
		}
		return res;
	}

	template <> inline
	Array* duplicateImpl(String* self, bool with_attributes) {
		R_PARAM(self);
		R_VAR(String*, res);

		res = String::doNew(self->length);
		for(int i = 0; i < self->length; i++) {
			res->content[i] = strdup(self->content[i]);
		}
		if(with_attributes && (self->attributes != ConstPool::Null)) {
			res->attributes = self->attributes->duplicate(true);
		}
		return res;
	}


	// subset definitions

	template <typename T, typename I>
	void extractSubset(T *elts, T* dstElts, I* idx, unsigned len, unsigned shift = 0) {
		R_PARAM(elts);
		R_PARAM(dstElts);
		R_PARAM(idx);
		R_VAR(String*, names);

		unsigned int i = len;
		int j = -1;
		if(RAttributes::getNames(elts) == ConstPool::Null) {

			while(i--) {
				j = convert<TYPE(I), r_int>(idx->content[i]) - 1;
				if(isOutOfBound(elts, j) < 0)
					dstElts->content[i+shift] = getNA<T>();
				else
					dstElts->content[i+shift] = elts->content[j];
			}

		} else {

			names = String::doNew(len);
			while(i--) {
				j = convert<TYPE(I), r_int>(idx->content[i]) - 1;
				if(isOutOfBound(elts, j) < 0) {
					dstElts->content[i+shift] = getNA<T>();
				} else {
					dstElts->content[i+shift] = elts->content[j];
				}
				names->content[i] = RAttributes::getNameAt(elts, j, true);
			}
			RAttributes::setNames(dstElts, names);

		}
	}

	template <typename T>
	void extractSubsetNoNames(T*dstElts, String *idx, unsigned len) {
		R_PARAM(dstElts);
		R_PARAM(idx);

		dstElts->content[0] = getNA<T>();
		// return vector NULL with size len and NO attributes.
		if(Any::isOfType<Vector>(T::ID)) {
			for(int i = 0; i < len; i++)
				dstElts->content[i] = dstElts->content[0];
			dstElts->attributes = ConstPool::Null;
			return;
		}

		idx->content[0] = (char*)ConstPool::NA_STRING;
		for(int i = 1; i < len; i++) {
			dstElts->content[i] = dstElts->content[0];
			idx->content[i] = (char*)ConstPool::NA_STRING;
		}
		RAttributes::createAndSetAttrById(dstElts, ATTR_NAMES, idx);
		return;
	}

	template <typename T>
	void extractSubset(T *elts, T*dstElts, String *idx, unsigned len, unsigned shift = 0) {
		R_PARAM(elts);
		R_PARAM(dstElts);
		R_PARAM(idx);
		int name_idx = -1;

		// there are no attributes so no names !
		if(elts->attributes == ConstPool::Null) {
			extractSubsetNoNames<T>(dstElts, idx, len);
			return ;
		}

		// fill-in dstElts with matching values of name index.
		for(int i = 0; i < len; i ++) {
			if((name_idx = RAttributes::getIndexByName(elts, idx->content[i]))) {
				dstElts->content[i] = getNA<T>();
				idx->content[i] = (char*)ConstPool::NA_STRING;
			} else {
				dstElts->content[i] = elts->content[name_idx];
			}
		}
		RAttributes::createAndSetAttrById(dstElts, ATTR_NAMES, idx);
	}

 	template <class T>
 	int isOutOfBound(T *elts, int idx_val) {
 			R_PARAM(elts);
 			return (idx_val < elts->length && idx_val >= 0) ? idx_val : -1;
 	}

 	template <class T>
 	int isOutOfBound(T *elts, char* idx_val) {
 			R_PARAM(elts);
 			int id = -1;
 			return (!strcmp("", idx_val)) || (!strcmp(ConstPool::NA_STRING, idx_val))
 			 					|| (id = RAttributes::getIndexByName(elts, idx_val)) ? -1 : id;
 	}

 	template <typename T, typename V, typename I>
 	T* updateSubsetByIndex(T *elts, V* values, I* idx, unsigned shift = 0) {
 		TmpPrimitiveSet<TYPE(I), TYPE(T)> *tmp_array = NULL;
 		R_PARAM(elts);
		R_PARAM(values);
		R_PARAM(idx);

		//	T *e = TArray<T>::getElements(elts);
 		unsigned int i = idx->length;
 		// Have to check if (i%(values->length))
 		rerror_on(values->length > i, "number of items to replace is not a multiple of replacement length.");

 		if((void*)values == (void*)elts){ // To prevent side effects
 			// FIXME: try a lightweight dup
 			values = static_cast<V*>(elts->duplicate());
 		} 

 		if((void*)elts == (void*)idx){ // To prevent side effects
			// FIXME: try a lightweight dup
 			idx = static_cast<I*>(idx->duplicate());
 		} 

 		int id = -1;
 		int j = -1;
 		while(i--) {
 			j = convert<TYPE(I), r_int>(idx->content[i]) - 1;
 			if(isOutOfBound<T>(elts, j) < 0) {
 				if(!tmp_array) { tmp_array = new TmpPrimitiveSet<TYPE(I), TYPE(T)>(i); }
 				tmp_array->addValue(j, convert<TYPE(V), TYPE(T)>(values->content[i + shift]));
 			} else {
 				elts->content[j]
 				              = convert<TYPE(V), TYPE(T)>(values->content[i + shift]);
 			}
 		}
 		if(tmp_array) {
 			elts = tmp_array->template appendTo<T>(elts);
 			delete tmp_array;
 		}
 		return elts;
 	}


 	/* Here we know that elts doesn't have attributes. */
 	template <typename T, typename V>
 	T* updateSubsetByIndexNoNames(T *elts, V* values, String* idx) {
 		R_PARAM(elts);
 		R_PARAM(values);
 		R_PARAM(idx);
 		int idx_names = 0;
 		int idx_val = 0;
 		int shift = elts->length;

 		elts = elts->enlarge(idx->length);

 		// create "names" attribute in idx, filters duplicated entries except NA or ""
 		// fills-in elts and resize it according to number of duplicated entries.
 		while(idx_names < idx->length) {
 			if(!strcmp(idx->content[idx_names], ConstPool::NA_STRING) ||
 					!strcmp(idx->content[idx_names], "")) {
 				elts->content[shift+idx_names] = convert<TYPE(V), TYPE(T)>(values->content[idx_val]);
 				idx_names++;
 				idx_val++;
 			}
 			else {
 				int j = 0;
 				for(j = 0; j < idx_names; j++) {
 					// idx->content[i] is a duplicate key index
 					if(!strcmp(idx->content[j], idx->content[idx_names])) {
 						elts->content[shift+j] = convert<TYPE(V), TYPE(T)>(values->content[idx_val]);
 						elts->length = elts->length - 1;
 						idx->lightShrinkAt(idx_names);
 						break;
 					}
 				}
 				// idx->content[i] is unique
 				if(idx_names == j) {
 					elts->content[shift+idx_names] = convert<TYPE(V), TYPE(T)>(values->content[idx_val]);
 					idx_names++;
 				}
 				idx_val++;
 			}
 		}
 		// finally set attribute, padding the 'shift' first names with ""
 		int orig_len = idx->length;
 		idx = idx->enlarge(shift);
 		memmove(idx->content+shift, idx->content, orig_len*sizeof(char*));
 		for(int k=0; k<shift; k++) idx->content[k] = (char*)"";
 		RAttributes::createAndSetAttrById(elts, ATTR_NAMES, idx);

 		return elts;
 	}


 	template <typename T, typename V, typename I>
 	T* updateSubsetByIndex(T *elts, V* values, String *idx) {
 		TmpPrimitiveSet<r_string, TYPE(T)> *tmp_array = NULL;
 		R_PARAM(elts);
 		R_PARAM(values);
 		R_PARAM(idx);
 		int id = 0;

 		if((void*)values == (void*)elts){ // To prevent side effects
 			// FIXME: try a lightweight dup
 			values = static_cast<V*>(elts->duplicate());
 		}

 		if((void*)elts == (void*)idx){ // To prevent side effects
 			// FIXME: try a lightweight dup
 			idx = static_cast<String*>(idx->duplicate());
 		}

 		//	T *e = TArray<T>::getElements(elts);
 		unsigned int len = idx->length;

 		rerror_on(values->length > len, "number of items to replace is not a multiple of replacement length.");

 		// It's an optimization, here we know that every value is a new one !
 		if(elts->attributes == ConstPool::Null) {
 			return updateSubsetByIndexNoNames<T, V>(elts, values, idx);
 		}

 		for(int i = 0; i < len; i++) {
 			if((id = isOutOfBound<T>(elts, idx->content[i])) < 0) {
 				// not in names attribute, have to create it in tmp_array
 				if(!tmp_array) {
 					tmp_array = new TmpPrimitiveSet<r_string, TYPE(T)>(len-i);
 				}
 				tmp_array->addValue(idx->content[i], convert<TYPE(V), TYPE(T)>(values->content[i]));
 			}
 			else {
 				elts->content[id] = convert<TYPE(V), TYPE(T)>(values->content[i]);
 			}
 		}

 		if(tmp_array) {
 			// merge elts with tmp_array
 			elts = tmp_array->template appendTo<T>(elts);
 			delete(tmp_array);
 		}
 		return elts;
 	}

template <typename T, typename U>
void coerce(T* src, U* dst, int offset = 0){ // FIXME use this one instead of copyAndCoerce
	for(int i = 0; i < src->length; i++) 
		dst->content[i + offset] = convert<TYPE(T), TYPE(U)>(src->content[i]);
}

#define COPY_IN(__src, __dst, __type)\
for(size -= i = static_cast<__type *>(__src)->length; i; i--)\
	__dst->content[size + i] = __type::doNewSingle(static_cast<__type *>(__src)->content[i]);

Vector *Vector::combine(int size, int nbData, Any** data){
	int i;
	R_VAR(Any*, argi);
	R_VAL(Vector*, self, Vector::doNew(size));

	while(nbData --){
		argi = data[nbData];
		int t = Any::getType(argi);
		if(Any::isOfType<Array>(t)){
			TYPECASE(t,
				Logical(COPY_IN(argi, self, Logical)),
				Int(COPY_IN(argi, self, Int)),
				Double(COPY_IN(argi, self, Double)),
				String(COPY_IN(argi, self, String)),
				Vector(COPY_IN(argi, self, Vector)), // FIXME here we need to duplicate not create !!
				Default(nyi_fatal()))
		} else {
			self->content[-- size] = argi->duplicate();
		}
	}

	return self;
}
#undef COPY_IN

template <class T>
T *allocAndCombine(int size, int nbData, Any** data){
	R_VAR(Any*, argi);
	R_VAR(T*, argiSameType);
	R_VAL(T*, self, T::doNew(size));

	const int id = T::ID;
	int len, t; 

	while(nbData --){
		argi = data[nbData];
		if((t = Any::getType(argi)) == id){
			argiSameType = static_cast<T*>(argi);
			len = argiSameType->length;
			size -= len;
			memcpy(self->content+size, argiSameType->content, len*sizeof(T::dataType));
		} else {
			switch(t >> SXP_LOW_PART){
				case Logical_ID>>SXP_LOW_PART:
					coerce(static_cast<Logical*>(argi), self, size -= static_cast<Logical*>(argi)->length);
				break;
				case Int_ID>>SXP_LOW_PART:
					coerce(static_cast<Int*>(argi), self, size -= static_cast<Logical*>(argi)->length);
				break;
				case Double_ID>>SXP_LOW_PART:
					coerce(static_cast<Double*>(argi), self, size -= static_cast<Logical*>(argi)->length);
				break;
				case String_ID>>SXP_LOW_PART:
					coerce(static_cast<String*>(argi), self, size -= static_cast<Logical*>(argi)->length);
				break;
				default:
					nyi_fatal();
			}
		}
	}
	return self;
}

 	template <typename T, typename U>
 	U* copyAndCoerce(T* self){
		R_PARAM(self);
		R_VAR(U*, res);

		res = U::doNew(self->length);
		for(int i = 0; i < res->length; i++) {
			res->content[i] = convert<TYPE(T), TYPE(U)>(self->content[i]);
		}
		res->attributes = self->attributes; // FIXME Duplicate attributes?
		return res;
 	}

 	template <typename T, typename U>
 	T* updateSubset(T *elts, Any* idx, U* values){
		R_PARAM(elts);
		R_PARAM(idx);
		R_PARAM(values);

 		switch(Any::getType(idx) >> SXP_LOW_PART){
			case Int::ID>>SXP_LOW_PART:
				return updateSubsetByIndex<T, U, Int>(elts, values, static_cast<Int*>(idx));
			case Double::ID >> SXP_LOW_PART:
 				return updateSubsetByIndex<T, U, Double>(elts, values, static_cast<Double*>(idx));
			case String::ID >> SXP_LOW_PART:
				return updateSubsetByIndex<T, U, String>(elts, values, static_cast<String*>(idx));
			case Logical::ID >> SXP_LOW_PART:
 				nyi_fatal();
 				return NULL;
 		}
 		rfatal("Subset assigment index type ("WORD") is not legal !", Any::getType(idx));
 		nyi_fatal();
 	}

 template <typename T>
 Any* subscriptAssignImplementation(T *self, int idx, Any *val){
	 R_PARAM(self);
	 R_PARAM(val);

 	int sid = T::ID;
 	int oid = Any::getType(val);

 	if(sid < oid){
 		nyi_fatal();
 		return 0;
 	} else {
 		switch(oid >> SXP_LOW_PART){
			case Logical::ID>>SXP_LOW_PART:
				T::setElement(self, convert<r_bool, TYPE(T)>(static_cast<Logical*>(val)->content[0]), idx);
				break;
			case Int::ID>>SXP_LOW_PART:
				T::setElement(self, convert<r_int, TYPE(T)>(static_cast<Int*>(val)->content[0]), idx);
				break;
			case Double::ID>>SXP_LOW_PART:
				T::setElement(self, convert<r_double, TYPE(T)>(static_cast<Double*>(val)->content[0]), idx);
				break;
			case String::ID>>SXP_LOW_PART:
				T::setElement(self, convert<r_string, TYPE(T)>(static_cast<String*>(val)->content[0]), idx);
				break;
 			default:
 				rfatal("Invalid replacement value.");
 		}
 		return self;
	}
}

template <typename T>
T* subsetImplementation(T *self, Any *idx){
 	R_PARAM(self);
	R_PARAM(idx);
	R_VAR(T*, res);
	rerror_on(!Any::isOfType<Array>(idx), "Subset index need to be an array ! but " WORD " found", Any::getType(idx));
	int i = static_cast<Array*>(idx)->length;
	switch(Any::getType(idx) >> SXP_LOW_PART){
		case Int::ID>>SXP_LOW_PART:
			res = T::doNew(i);
			extractSubset<T, Int>(self, res, static_cast<Int*>(idx), i);
			break;
		case Double::ID >> SXP_LOW_PART:
			res = T::doNew(i);
			extractSubset<T, Double>(self, res, static_cast<Double*>(idx), i);
			break;
		case String::ID >> SXP_LOW_PART:
			res = T::doNew(i);
			extractSubset<T, String>(self, res, static_cast<String*>(idx), i);
			break;
		case Logical::ID >> SXP_LOW_PART:
			nyi_fatal();
			break;
		default:
			rfatal("Subset by this type ("WORD") is not legal !", Any::getType(idx));
	}
	return res;
}

template <typename T>
Any* subsetAssignImplementation(Any *self_, Any *idx, Any *values){
	R_PARAM(self_);
	R_PARAM(idx);
	R_PARAM(values);
	R_VAR(T*, self);

	int sid = T::ID;
	int oid = Any::getType(values);
	self = static_cast<T*>(self_);

	if(sid < oid){
		// FIXME do updateSubset, but we know that conversion is useless !
		// Have to find a way to remove conversions.
		switch(oid >> SXP_LOW_PART){
			case Logical_ID>>SXP_LOW_PART:
				rfatal("how can this happen ?");
			case Int_ID>>SXP_LOW_PART:
				self_ = copyAndCoerce<T, Int>(self);
				self_ = updateSubset<Int, Int>(static_cast<Int*>(self_), idx, static_cast<Int*>(values));
				break;
			case Double_ID>>SXP_LOW_PART:
				self_ = copyAndCoerce<T, Double>(self);
				self_ = updateSubset<Double, Double>(static_cast<Double*>(self_), idx, static_cast<Double*>(values));
				break;
			case String_ID>>SXP_LOW_PART:
				self_ = copyAndCoerce<T, String>(self);
				self_ = updateSubset<String, String>(static_cast<String*>(self_), idx, static_cast<String*>(values));
				break;
		}
	} else {
		switch(oid >> SXP_LOW_PART){
			case Logical_ID>>SXP_LOW_PART:
				self_ = updateSubset<T, Logical>(self, idx, static_cast<Logical*>(values));
				break;
			case Int_ID>>SXP_LOW_PART:
				self_ = updateSubset<T, Int>(self, idx, static_cast<Int*>(values));
				break;
			case Double_ID>>SXP_LOW_PART:
				self_ = updateSubset<T, Double>(self, idx, static_cast<Double*>(values));
				break;
			case String_ID>>SXP_LOW_PART:
				self_ = updateSubset<T, String>(self, idx, static_cast<String*>(values));
				break;
			default:
				rfatal("Invalid replacement values");
		}
	}
	return self_;
}

//	TODO Warning, for String an Vector, may need a
//	specialization for content duplication.
//	FIXME In this version, subscript recursion is not taken in account.
//	Have to understand better how it works to implement.
template <class T> inline
T * doSubscript(T* self, int idx) {
		R_PARAM(self);
		R_VAR(T *, res);
		res = T::doNewSingle(self->getElement(idx));
		return res;
}

// full definitions

#define min_and_max(t_name, t_parent, t_content, elmt_gc_root, op)			\
	asSelf(t_name);																												\
	R_VAR(t_name*, obj);																									\
	if(!self->length) {																										\
		warning("no non-missing arguments to max; returning -Inf");					\
		return ((t_content)0);	                   													\
	}																																			\
	t_content res = self->content[0];																			\
	for(uint32_t i=1; i<self->length; i++)																\
		if(res op self->content[i])																					\
			res = self->content[i];																						\
	return res


#define all_concrete(t_name, t_parent, t_content, elmt_gc_root)					\
	t_name* t_name::doNew(uint32_t length) {															\
		R_VAR(t_name*, res);																								\
		res = static_cast<t_name*>(operator new(sizeof(t_name) + (length)*sizeof(t_content) - sizeof(t_content), virtualVT)); \
		res->length = length;																								\
		res->attributes = ConstPool::Null;																	\
		return res;																													\
	}																																			\
																																				\
	t_name* t_name::doNewSingle(t_content val) {													\
		R_VAR(t_name*, res);																								\
		elmt_gc_root(val, 0);																								\
		res = doNew(1);																											\
		res->content[0] = val;																							\
		res->attributes = ConstPool::Null;																	\
		return res;																													\
	}																																			\
																																				\
	/* Just enlarge array content, do not duplicate attributes but */			\
	/* enlarge names attributes which is dependent of array's length */		\
	t_name* t_name::enlarge(uint32_t n) {																	\
		asSelf(t_name);																											\
		R_VAR(t_name*, res);																								\
		R_VAR(String*, names);																							\
		res = t_name::doNew(self->getLength() + n);													\
		memcpy(res->content, self->content, sizeof(t_content)*(self->getLength()));	\
		res->attributes = self->attributes;																	\
		if((names = RAttributes::getNames(res)) != ConstPool::Null)					\
			RAttributes::setNames(res, names->enlarge(n));										\
		return res;																													\
	}																																			\
																																				\
	void t_name::lightShrinkAt(uint32_t n) {															\
		asSelf(t_name);																											\
		R_VAR(Vector*, attr);																								\
		R_VAR(String*, nom);																								\
		int len = self->length;																							\
		Assert(n < len && n >= 0, "Trying to shrink at bad position: '%d'. Max: %d", n, len);	\
		len--;																															\
		if(len > 0) {																												\
			memmove(self->content + n, self->content + n + 1, sizeof(t_content)*(len - n));	\
		}																																		\
		self->length = len;																									\
	}																																			\
																																				\
	t_name* t_name::shrinkAt(uint32_t n, bool with_attributes) {					\
	asSelf(t_name);																												\
	R_VAR(t_name*, res);																									\
	R_VAR(Vector*, attr);																									\
	R_VAR(String*, nom);																									\
	Assert(n < self->length && n >= 0, "Trying to shrink at bad position: '%d'. Max: %d", n, self->length); \
	res = t_name::doNew(self->length - 1);																\
	if(res->length > 0) {																									\
		memcpy(res->content, self->content, sizeof(t_content)*n);						\
		memcpy(res->content + n, self->content + n + 1, sizeof(t_content)*(res->length - n)); \
	}																																			\
	if(with_attributes && self->attributes) {															\
		res->attributes = self->attributes; /*FIXME res->setAttributes(self->attributes)*/	\
		/* set attributes names with a shrinked value at index n */							\
		static_cast<Vector*>(res->attributes)->setElement(									\
					static_cast<String*>(static_cast<Vector*>(self->attributes)->getElement(0))->	\
											shrinkAt(n - NB_Attributes),	                    \
					0);																														\
	}																																			\
	return res;																														\
	}																																			\
																																				\
	Array* t_name::duplicate(bool with_attributes) {											\
		asSelf(t_name);																											\
		return duplicateImpl<t_name>(self, with_attributes);								\
	}																																			\
																																				\
	t_content t_name::getElement(uint32_t i) {														\
		asSelf(t_name);																											\
		Assert(i >= 0 && i < self->length,\
				"Index out of bounds: '%d'. Max: %d", i, self->length);         \
		t_content res;																											\
		elmt_gc_root(res, 0);																								\
		res = self->content[i];																							\
		return res;																													\
	}																																			\
																																				\
	void t_name::setElement(t_content value, uint32_t i) {								\
		asSelf(t_name);																											\
		elmt_gc_root(value, 0);																							\
		Assert(i >= 0 && i < self->length,\
				"Index out of bounds: '%d'. Max: %d", i, self->length);         \
		self->content[i] = value;																						\
	}																																			\
																																				\
	t_name *t_name::newInstance(uint32_t size) {													\
		asSelf(t_name);																											\
		R_VAR(t_name*, res);																								\
		res = t_name::doNew(size);																					\
		return res;																													\
	}																																			\
																																				\
	t_name *t_name::newInstanceWith(uint32_t size, ...) {									\
		asSelf(t_name);																											\
		R_VAR(t_name*, res);																								\
		va_list idxs;                                                       \
		res = t_name::doNew(size);																					\
		va_start(idxs, size);																								\
		while(size--)                                                       \
			res->setElement(self->getElement(va_arg(idxs, int)), size);			  \
		va_end(idxs);																												\
		return res;																													\
	}																																			\
																																				\
	Array* t_name::reverse() {																						\
		asSelf(t_name);																											\
		R_VAR(t_name*, res);																								\
		res = t_name::doNew(self->length);																	\
		for(uint32_t i=0; i<self->length; i++)															\
			res->content[self->length-i-1] = self->content[i];								\
		return res;																													\
	}                                                                     \

	int checkAndGetSubscriptIndex(Array* self, Any* idx, bool assign = false) {
		R_PARAM(self);
		R_PARAM(idx);
		int idx_val = -1;

		rerror_on(!Any::isOfType<Array>(idx), "Subscript index need to be an array, but " WORD " found", Any::getType(idx));
		rerror_on(Any::isOfType<Vector>(idx), "invalid subscript type 'list'");
		if(Any::isOfType<String>(idx)) {
			if((idx_val = RAttributes::getIndexByName(self, static_cast<String*>(idx)->getElement(0))) < 0) {
				return (assign? self->length : -1);
			}
		}
		else {
			idx_val = static_cast<Array*>(idx)->toIntOne();
			if(idx_val <= 0 || (!assign && idx_val > self->length)){
				rerror_on(idx_val == 0, "attempt to select less than one element");
				rerror_on(idx_val < 0, "attempt to select more than one element");
				rerror_on(idx_val > self->length && !assign,
				"subscript out of bounds: '%d'. Max: %d", idx_val, self->length);
			}
			idx_val--; /* Because R is 1 indexed */
		}
		return idx_val;
	}


#define all_concrete_but_vector(t_name, t_parent, t_content, elmt_gc_root) \
	all_concrete(t_name, t_parent, t_content, elmt_gc_root)								\
																																				\
	r_int t_name::toIntOne() {																						\
		asSelf(t_name);																											\
		return convert<t_content, r_int>(self->getElement(0));							\
	}																																			\
																																				\
	r_bool t_name::toLogicalOne() {																				\
		asSelf(t_name);																											\
		return convert<t_content, r_bool>(self->getElement(0));							\
	}																																			\
																																				\
	Any* t_name::subscript(Any* idx, bool with_check) {										\
		asSelf(t_name);	R_PARAM(idx); R_VAR(t_name *, res);									\
		int idx_val = -1;																										\
																																				\
		if(with_check) {																										\
			if((idx_val = checkAndGetSubscriptIndex(self, idx)) < 0) 					\
				return ConstPool::Null;																					\
		} else                                                              \
			idx_val = static_cast<Array*>(idx)->toIntOne() - 1; 					    \
																																				\
		return doSubscript<t_name>(self, idx_val);			 										\
	}																																			\
																																				\
	Any* t_name::subscriptAssign(Any* idx_val, Any* val, Any** out_this) {\
		asSelf(t_name);																											\
		R_PARAM(idx_val);																										\
		R_VAR(t_name*, res);																								\
																																				\
		int sid = t_name::ID;																								\
		int oid = Any::getType(val);																				\
		int idx = -1;										                                		\
		int len = self->length;																							\
		t_content res_val;																									\
																																				\
		if((idx = checkAndGetSubscriptIndex(self, idx_val, true)) >= len)		\
			nyi_fatal();																											\
																																				\
		if(sid < oid) {																											\
			nyi_fatal();																											\
			switch(sid >> SXP_LOW_PART){																			\
				case Logical::ID>>SXP_LOW_PART:																	\
					*out_this = copyAndCoerce<t_name, Logical>(self);							\
					static_cast<Logical*>(*out_this)->content[idx] = static_cast<Logical*>(val)->getElement(0); \
					break;																												\
				case Int::ID>>SXP_LOW_PART:																			\
					*out_this = copyAndCoerce<t_name, Int>(self);									\
					static_cast<Int*>(*out_this)->content[idx] = static_cast<Int*>(val)->getElement(0);	\
					break;																												\
				case Double::ID>>SXP_LOW_PART:																	\
					*out_this = copyAndCoerce<t_name, Double>(self);								\
					static_cast<Double*>(*out_this)->content[idx] = static_cast<Double*>(val)->getElement(0);	\
					break;																												\
				case String::ID>>SXP_LOW_PART:																	\
					*out_this = copyAndCoerce<t_name, String>(self);								\
					static_cast<String*>(*out_this)->content[idx] = static_cast<String*>(val)->getElement(0);	\
					break;																												\
				default:																												\
					rfatal("Invalid replacement value.");													\
			}																																	\
			return val;																												\
		} else {																														\
			switch(oid >> SXP_LOW_PART){																			\
				case Logical::ID>>SXP_LOW_PART:																	\
					res_val = convert<r_bool, t_content>(static_cast<Logical*>(val)->getElement(0)); \
					self->content[idx] = res_val;																	\
					break;																												\
				case Int::ID>>SXP_LOW_PART:																			\
					res_val = convert<r_int, t_content>(static_cast<Int*>(val)->getElement(0));	\
					self->content[idx] = res_val;																	\
					break;																												\
				case Double::ID>>SXP_LOW_PART:																	\
					res_val = convert<r_double, t_content>(static_cast<Double*>(val)->getElement(0));	\
					self->content[idx] = res_val;																	\
					break;																												\
				case String::ID>>SXP_LOW_PART:																	\
					res_val = convert<r_string, t_content>(static_cast<String*>(val)->getElement(0));	\
					self->content[idx] = res_val;																	\
					break;																												\
				default:																												\
					rfatal("Invalid replacement value.");													\
			}																																	\
			*out_this = self;																									\
			res = t_name::doNewSingle(res_val);																\
			return res;																												\
		}																																		\
	}																																			\
																																				\
	Any* t_name::subset(Any *idx) {																				\
		asSelf(t_name);                                                     \
		R_PARAM(idx);				                                                \
		return subsetImplementation<t_name>(self, idx);                     \
	}																																			\
																																				\
	Any* t_name::subsetAssign(Any *set, Any *values) {										\
		asSelf(t_name);                                                     \
		R_PARAM(values);				                                            \
		return subsetAssignImplementation<t_name>(self, set, values);       \
	}																																			\
                                                                        \
  t_name *t_name::combine(int size, int nbData, Any** data){            \
   return allocAndCombine<t_name>(size, nbData, data);                  \
	}


#define array_parent(t_name, t_parent, t_content, elmt_gc_root)

#define array_number(t_name, t_parent, t_content, elmt_gc_root)

#define array_string(t_name, t_parent, t_content, elmt_gc_root)					\
	all_concrete_but_vector(t_name, t_parent, t_content, elmt_gc_root)		\
	int String::containsString(char* search) {														\
		for(int i = 0; i < length; i++) if(!strcmp(content[i], search)) return i;	\
		return -1;																													\
	}

#define array_vector(t_name, t_parent, t_content, elmt_gc_root)					\
	all_concrete(t_name, t_parent, t_content, elmt_gc_root)								\
																																				\
	Any* t_name::subscript(Any* idx, bool with_check) {										\
		asSelf(t_name);	R_PARAM(idx); R_VAR(t_name *, res);									\
		int idx_val = -1;																										\
																																				\
		if(with_check) {																										\
			if((idx_val = checkAndGetSubscriptIndex(self, idx)) < 0) 					\
				return ConstPool::Null;																					\
		} else                                                              \
			idx_val = static_cast<Array*>(idx)->toIntOne() - 1; 					    \
																																				\
		return self->getElement(idx_val);                                   \
	}																																			\
	

	// FIXME: return inf, can have a NA
#define array_numbers(t_name, t_parent, t_content, elmt_gc_root)				\
	all_concrete_but_vector(t_name, t_parent, t_content, elmt_gc_root)		\
																																				\
	t_content t_name::max() {																							\
		min_and_max(t_name, t_parent, t_content, elmt_gc_root, <);					\
	}																																			\
																																				\
	t_content t_name::min() {																							\
		min_and_max(t_name, t_parent, t_content, elmt_gc_root, >);					\
	}																																			\
																																				\
	t_name* t_name::opposite() {																					\
		asSelf(t_name);																											\
		R_VAR(t_name*, res);																								\
		res = t_name::doNew(self->length);																	\
		for(uint32_t i=0; i<self->length; i++)															\
			res->content[i] = -self->content[i];															\
		return res;																													\
	}																																			\
																																				\
	Logical* t_name::neg() {																							\
		asSelf(t_name);																											\
		R_VAR(Logical*, res);																								\
		res = Logical::doNew(self->length);																	\
																																				\
		for(uint32_t i=0; i<self->length; i++) {														\
			t_content val = self->content[i];																	\
			res->content[i] = (val == ConstPool::NA_LOG) ? val : !val;				\
		}																																		\
		return res;																													\
	}


#define defineArray(t_name, t_parent, t_content, elmt_gc_root, local_decl) \
																																				\
	local_decl(t_name, t_parent, t_content, elmt_gc_root)									\

	on_arrays(defineArray);

	Any* Array::newSingleInstance() {																			
		asSelf(Array);																											
		return self->newInstance(1);																				
	}																																			

	uint32_t Array::getLength() {
		asSelf(Array);
		return self->length;
	}

}
